package poolserver

import (
	"encoding/binary"
	"errors"
	"fmt"
	"sync/atomic"

	"go.uber.org/zap"
)

func ExtractPrepareName(buf []byte) string {
	var pos int32
	var begin int32
	var prepareName string
	if buf[0] == 'P' {
		pos = 5
		for pos = 5; buf[pos] != 0; pos++ {
		}
		prepareName = string(buf[5:pos])
		pos++
		begin = pos
		for pos = begin; buf[pos] != 0; pos++ {
		}
		//zap.S().Infof("SQL=%s", string(buf[begin:pos]))
	} else if buf[0] == 'B' {
		/*第一个字段是portalName，通常portalName为空*/
		pos = 5
		for pos = 5; buf[pos] != 0; pos++ {
		}
		if buf[pos] != 0 {
			zap.S().Panicf("BUG******: portal not empty!!!")
		}
		pos++
		/*这后面是prepare的名称*/
		begin = pos
		for ; buf[pos] != 0; pos++ {
		}
		prepareName = string(buf[begin:pos])
	} else if buf[0] == 'D' {
		pos = 6
		for pos = 6; buf[pos] != 0; pos++ {
		}
		prepareName = string(buf[6:pos])
	}
	return prepareName
}

func (ctx *ConnContext) CumulatePrepare() {
	var i int32
	var prepareName string
	var cutPos int32
	var cutLen int32
	var tmpPrepareLen int32
	var tmpPrepareData []byte
	var tmpBackendPrepareId int32
	//var backendPrepareName string

	for i = 0; i < ctx.msgCount; i++ {
		if ctx.msgList[i][0] == 'P' {
			prepareName = ExtractPrepareName(ctx.msgList[i])

			if prepareName == "" { /*处理unnamed prepared statement的情况*/
				ctx.isInUnnamedPrepared = true
				tmpPrepareLen = int32(len(ctx.msgList[i]))
				ctx.unnamePrepareData = make([]byte, tmpPrepareLen)
				copy(ctx.unnamePrepareData, ctx.msgList[i])
				continue
			} else {
				ctx.isInUnnamedPrepared = false
				ctx.unnamePrepareData = nil
			}

			cutPos = int32(5 + len([]byte(prepareName)) + 1)
			cutLen = int32(len(ctx.msgList[i])) - cutPos

			// 5字节的消息头，12字节的prepareName
			tmpPrepareLen = cutLen + 5 + 12
			tmpPrepareData = make([]byte, tmpPrepareLen)
			copy(tmpPrepareData[17:], ctx.msgList[i][cutPos:])
			//copy(tmpPrepareData[tmpPrepareLen-5:], []byte{'S', 0, 0, 0, 4})

			tmpBackendPrepareId = ctx.pBackConn.AllocPrepareId()
			//backendPrepareName = fmt.Sprintf("S%d", tmpBackendPrepareId)

			cuPre := new(CuPre)
			cuPre.prepareRequestLen = tmpPrepareLen
			cuPre.prepareRequestData = tmpPrepareData
			cuPre.backendPrepareId = tmpBackendPrepareId
			ctx.cupreMap[prepareName] = cuPre

		}
	}

}

func rewritePrepareReq(backendPrepareName string, srcBuf []byte, dstBuf []byte) int32 {
	var pos int32
	var packetLen int32
	var oldPrepareNameLen int32
	var backendPrepareNameLen int32
	//var prepareName string
	var cutPos int32

	/*第一个字段是prepareName*/
	pos = 5
	for pos = 5; srcBuf[pos] != 0; pos++ {
	}
	oldPrepareNameLen = pos - 5
	//prepareName = string(srcBuf[5:pos])
	pos++
	cutPos = pos

	//生产新的prepare数据包的包头
	backendPrepareNameLen = int32(len([]byte(backendPrepareName)))

	/*prepare request包的长度，旧S包的长度+ （新旧包的prepareName的长度差）*/
	packetLen = int32(binary.BigEndian.Uint32(srcBuf[1:5])) + backendPrepareNameLen - oldPrepareNameLen

	dstBuf[0] = srcBuf[0]
	binary.BigEndian.PutUint32(dstBuf[1:], uint32(packetLen))
	copy(dstBuf[5:], []byte(backendPrepareName))
	dstBuf[5+backendPrepareNameLen] = 0
	copy(dstBuf[5+backendPrepareNameLen+1:], srcBuf[cutPos:])
	return 1 + packetLen
}

/*
重写bind消息包
*/
func rewriteBindReq(backendPrepareName string, srcBuf []byte, dstBuf []byte) int32 { /*重写P消息*/
	var pos int32
	var portalName []byte
	var portalNameLen int32
	var begin int32
	//var prepareName string
	var oldPrepareNameLen int32
	var backendPrepareNameLen int32
	var packetLen int32
	var cutPos int32

	/*第一个字段是portalName，通常portalName为空*/
	pos = 5
	for pos = 5; srcBuf[pos] != 0; pos++ {
	}
	portalName = make([]byte, pos-5)
	copy(portalName, srcBuf[5:pos])
	portalNameLen = pos - 5
	pos++
	/*这后面是prepare的名称*/
	begin = pos
	for ; srcBuf[pos] != 0; pos++ {
	}
	//prepareName = string(srcBuf[begin:pos])
	oldPrepareNameLen = pos - begin
	pos++
	cutPos = pos

	backendPrepareNameLen = int32(len([]byte(backendPrepareName)))
	/*新包的长度 = 旧包的长度 +（新旧包的prepareName的长度差）*/
	packetLen = int32(binary.BigEndian.Uint32(srcBuf[1:5])) + backendPrepareNameLen - oldPrepareNameLen

	dstBuf[0] = srcBuf[0]
	binary.BigEndian.PutUint32(dstBuf[1:], uint32(packetLen))
	if portalNameLen > 0 {
		copy(dstBuf[5:], portalName)
	}
	pos = 5 + portalNameLen
	dstBuf[pos] = 0
	pos++
	copy(dstBuf[pos:], []byte(backendPrepareName))
	pos += backendPrepareNameLen
	dstBuf[pos] = 0
	pos++
	copy(dstBuf[pos:], srcBuf[cutPos:])

	return packetLen + 1
}

/*
重写Describe消息包
返回：prepareName
*/
func rewriteDescribeReq(backendPrepareName string, srcBuf []byte, dstBuf []byte) int32 { /*重写D消息*/
	var pos int32
	var packetLen int32
	var oldPrepareNameLen int32
	var backendPrepareNameLen int32
	var cutPos int32

	/*应该是'S', 然后就是prepare name*/
	pos = 6
	for pos = 6; srcBuf[pos] != 0; pos++ {
	}
	oldPrepareNameLen = pos - 6
	//prepareName = string(srcBuf[6:pos])
	pos++
	cutPos = pos

	backendPrepareNameLen = int32(len([]byte(backendPrepareName)))
	/*prepare request包的长度，旧S包的长度+ （新旧包的prepareName的长度差）*/
	packetLen = int32(binary.BigEndian.Uint32(srcBuf[1:5])) + backendPrepareNameLen - oldPrepareNameLen
	/* prepare request包的长度 = P消息中长度域+1，+1是因为消息的第一个字节标识消息类型*/

	dstBuf[0] = srcBuf[0]
	dstBuf[5] = srcBuf[5]
	binary.BigEndian.PutUint32(dstBuf[1:], uint32(packetLen))
	copy(dstBuf[6:], []byte(backendPrepareName))
	dstBuf[6+backendPrepareNameLen] = 0
	pos = 6 + backendPrepareNameLen + 1
	copy(dstBuf[pos:], srcBuf[cutPos:])

	return 1 + packetLen
}

/*则重新发prepare数据到这个后端上*/
func (ctx *ConnContext) reSendPrepareData(needPrepareMap map[string]*PrepareIdData) error {
	var err error
	var newPrepareNameLen int32
	var backendPrepareName string
	var beginPos int32
	var packetLen int32
	var PrepareRequest []byte
	var n int32
	var isSuccess bool

	for prepareName, prepareIdData := range needPrepareMap {
		if prepareName == "" { /*这是未命名的prepare*/
			beginPos = 0
			PrepareRequest = prepareIdData.PrepareRequest
		} else {
			zap.S().Infof("Client(%d): SB(%d): reprepare %s", ctx.Pid, ctx.pBackConn.Id, prepareName)
			backendPrepareName = fmt.Sprintf("S%d", prepareIdData.PrepareId)
			//backendPrepareName = fmt.Sprintf("S%d", pib.PrepareId)
			newPrepareNameLen = int32(len([]byte(backendPrepareName)))
			beginPos = 11 - newPrepareNameLen
			PrepareRequest = prepareIdData.PrepareRequest
			PrepareRequest[beginPos] = 'P'
			packetLen = int32(len(PrepareRequest)) - 17 + newPrepareNameLen + 1 + 4
			binary.BigEndian.PutUint32(PrepareRequest[beginPos+1:], uint32(packetLen))
			copy(PrepareRequest[beginPos+5:], []byte(backendPrepareName))
			PrepareRequest[beginPos+5+newPrepareNameLen] = 0
		}

		//TCHDEBUG zap.S().Infof("Client(%d): need send P(%s) to backend(%d), new prepare name=%s",
		//TCHDEBUG 	ctx.Pid, prepareName, ctx.pBackConn.Id, backendPrepareName)

		_, err = sendData(ctx.pBackConn.Conn, PrepareRequest[beginPos:])
		if err != nil {
			zap.S().Infof("Client(%d): SB(%d): ERROR: %s", ctx.Pid, ctx.pBackConn.Id, err.Error())
			ctx.cliConn.Close()
			zap.S().Infof("Client(%d): reconnect backend(%d)", ctx.Pid, ctx.pBackConn.Id)
			ctx.BeReconnect()
			if ctx.isGetBackend {
				zap.S().Debugf("Client(%d): release backend(%d)", ctx.Pid, ctx.pBackConn.Id)
				atomic.StoreInt32(&ctx.pBackConn.State, 0)
				ctx.isGetBackend = false
			}
			return err
		}
	}
	/*最后再发一个sync消息*/
	_, err = sendData(ctx.pBackConn.Conn, []byte{'S', 0, 0, 0, 4})
	if err != nil {
		zap.S().Infof("Client(%d): SB(%d): ERROR: %s", ctx.Pid, ctx.pBackConn.Id, err.Error())
		ctx.cliConn.Close()
		zap.S().Infof("Client(%d): reconnect backend(%d)", ctx.Pid, ctx.pBackConn.Id)
		ctx.BeReconnect()
		if ctx.isGetBackend {
			zap.S().Debugf("Client(%d): release backend(%d)", ctx.Pid, ctx.pBackConn.Id)
			atomic.StoreInt32(&ctx.pBackConn.State, 0)
			ctx.isGetBackend = false
		}
		return err
	}

	isSuccess = true
	for {
		//TCHDEBUG zap.S().Infof("Client(%d, B): RB(%d): begin recv...", ctx.Pid, ctx.pBackConn.Id)
		n, _, err = recvMessage(ctx.pBackConn.Conn, ctx.recvBuf[0:])
		if err != nil {
			zap.S().Infof("Client(%d, B): RB(%d): ERROR: len=%d, %s", ctx.Pid, ctx.pBackConn.Id, n, err.Error())
			ctx.cliConn.Close()
			ctx.BeReconnect()
			if ctx.isGetBackend {
				zap.S().Debugf("Client(%d): release backend(%d)", ctx.Pid, ctx.pBackConn.Id)
				atomic.StoreInt32(&ctx.pBackConn.State, 0)
				ctx.isGetBackend = false
			}
			return err
		}

		if ctx.recvBuf[0] == 'E' {
			isSuccess = false
			printBackendErrorMessage(ctx.recvBuf[:])
		}

		//TCHDEBUG zap.S().Infof("Client(%d, B): RB(%d, %c): data=%v", ctx.Pid, ctx.pBackConn.Id, ctx.recvBuf[0], ctx.recvBuf[:n])
		if ctx.recvBuf[0] == 'Z' {
			break
		}
	}

	if isSuccess {
		for prepareName, prepareIdData := range needPrepareMap {
			if prepareName == "" {
				continue
			}
			pdata, ok := ctx.cachedPrepare.Get(prepareName)
			if !ok {
				zap.S().Panicf("BUG!!!!!!")
			}
			prepareInBackend := new(PrepareInBackend)
			prepareInBackend.PrepareId = prepareIdData.PrepareId
			prepareInBackend.BackConn = ctx.pBackConn
			prepareInBackend.ReConnCnt = ctx.pBackConn.ReConnCnt
			pdata.BackendMap[ctx.pBackConn.Id] = prepareInBackend
			zap.S().Infof("Client(%d): store prepare name=%s to backend(%d), backendPrepareId=%d(reprepare)",
				ctx.Pid, prepareName, ctx.pBackConn.Id, prepareIdData.PrepareId)
		}
	}

	return nil
}

func (ctx *ConnContext) ProcessExtendedQuery() error { /* 处理Extended Query消息 */
	var err error
	var i int32
	var prepareName string
	var backendPrepareName string
	var pos int32
	var pdata *PrepareData
	var pib *PrepareInBackend

	var needPrepareMap map[string]*PrepareIdData
	var prepareId int32

	var haveUnnamedP bool  /*在请求中是否有未命名的prepare消息*/
	var haveUnnamedBD bool /*在请求中是否有未命名的BIND或Describe消息*/

	/* 现获得一个backend */
	if !ctx.isGetBackend {
		ctx.pBackConn = getBackend(ctx.pool)
		zap.S().Debugf("Client(%d): get backend(%d)", ctx.Pid, ctx.pBackConn.Id)
		ctx.isGetBackend = true
		//TCHDEBUG zap.S().Infof("Client(%d): hold backend(%d)", ctx.Pid, ctx.pBackConn.Id)
	}

	ctx.CumulatePrepare()
	needPrepareMap = make(map[string]*PrepareIdData)

	haveUnnamedP = false
	haveUnnamedBD = false

	/*把重写过的数据放到ctx.dstBuf中*/
	pos = 0
	for i = 0; i < ctx.msgCount; i++ {
		if ctx.msgList[i][0] == 'P' {
			//TCHDEBUG zap.S().Infof("Client(%d), SQL=%s", ctx.Pid, ExtractSQL(ctx.msgList[i]))
		}

		if ctx.msgList[i][0] == 'P' || ctx.msgList[i][0] == 'B' || ctx.msgList[i][0] == 'D' {
			prepareName = ExtractPrepareName(ctx.msgList[i])
			if ctx.msgList[i][0] == 'P' && prepareName == "" {
				haveUnnamedP = true
			}

			if (ctx.msgList[i][0] == 'B' || ctx.msgList[i][0] == 'D') && prepareName == "" {
				haveUnnamedBD = true
			}

			notNeedRewrite := false
			if prepareName == "" { /*匿名的prepare*/
				notNeedRewrite = true
			}

			if ctx.msgList[i][0] == 'D' && ctx.msgList[i][5] == 'P' {
				notNeedRewrite = true
			}
			if notNeedRewrite {
				copy(ctx.sendBuf[pos:], ctx.msgList[i])
				pos += int32(len(ctx.msgList[i]))
				continue
			}

			if cuPre, ok := ctx.cupreMap[prepareName]; ok {
				prepareId = cuPre.backendPrepareId
			} else if prepareIdData, ok := needPrepareMap[prepareName]; ok { /*前面已经记录过*/
				prepareId = prepareIdData.PrepareId
			} else {
				pdata, ok = ctx.cachedPrepare.Get(prepareName)
				if !ok { /* 发现一个不存在的prepare */
					err = ctx.handlePrepareNameNotExists(prepareName)
					return err
				}

				pib, ok = pdata.BackendMap[ctx.pBackConn.Id]
				if !ok { /*未被parse过，需要parse*/
					prepareId = ctx.pBackConn.AllocPrepareId()
					prepareIdData := new(PrepareIdData)
					prepareIdData.PrepareId = prepareId
					prepareIdData.PrepareRequest = pdata.PrepareRequest
					needPrepareMap[prepareName] = prepareIdData
				} else {
					prepareId = pib.PrepareId
					if pib.ReConnCnt != pib.BackConn.ReConnCnt { /*说明这个后端连接重新连接过，需要重新发送prepare命令*/
						prepareIdData := new(PrepareIdData)
						prepareIdData.PrepareId = prepareId
						prepareIdData.PrepareRequest = pdata.PrepareRequest
						needPrepareMap[prepareName] = prepareIdData
					}
				}
			}
			backendPrepareName = fmt.Sprintf("S%d", prepareId)
			if ctx.msgList[i][0] == 'P' {
				pos += rewritePrepareReq(backendPrepareName, ctx.msgList[i], ctx.sendBuf[pos:])
			} else if ctx.msgList[i][0] == 'B' {
				pos += rewriteBindReq(backendPrepareName, ctx.msgList[i], ctx.sendBuf[pos:])
			} else if ctx.msgList[i][0] == 'D' {
				pos += rewriteDescribeReq(backendPrepareName, ctx.msgList[i], ctx.sendBuf[pos:])
			}

		} else {
			copy(ctx.sendBuf[pos:], ctx.msgList[i])
			pos += int32(len(ctx.msgList[i]))
		}
	}
	ctx.sendLen = pos

	if (!haveUnnamedP) && haveUnnamedBD { /* 当请求中没有未命名的P消息，而只有未命名的B或D消息，需要重新发未命名的P消息到后端*/
		if ctx.Pid != ctx.pBackConn.LastFrontId {
			prepareId = -1
			prepareIdData := new(PrepareIdData)
			prepareIdData.PrepareId = prepareId
			prepareIdData.PrepareRequest = ctx.unnamePrepareData
			needPrepareMap[""] = prepareIdData
		}
	}
	ctx.pBackConn.LastFrontId = ctx.Pid

	if len(needPrepareMap) > 0 {
		err = ctx.reSendPrepareData(needPrepareMap)
		if err != nil {
			return err
		}
	}

	err = ctx.sendToBackend()
	if err != nil {
		return err
	}
	var clientErr error = nil
	var backendErr error = nil
	err = nil
	clientErr, backendErr = ctx.forwardToClient()
	if clientErr != nil {
		err = clientErr
	} else if backendErr != nil {
		err = backendErr
	}
	return err
}

func (ctx *ConnContext) handlePrepareNameNotExists(prepareName string) error {
	var err error
	var n int32

	zap.S().Panicf("Client(%d, B): ERROR: unknown prepare name=%s", ctx.Pid, prepareName)
	/* 先从客户端把后面的数据包接收完，然后再发送错误消息到前端 */
	for {
		//TCHDEBUG zap.S().Infof("Client(%d, B): RC: begin ...", ctx.Pid)
		n, _, err = recvMessage(ctx.cliConn, ctx.recvBuf[:])
		if err != nil {
			noused(n)
			zap.S().Infof("Client(%d, B): RC: ERROR : %s", ctx.Pid, err.Error())
			ctx.cliConn.Close()
			/* 做一些清理工作，把此后端连接上的事务回滚掉*/
			if ctx.transState != 'I' {
				zap.S().Infof("Client(%d, B): Cleanup backend(%d) transaction", ctx.Pid, ctx.pBackConn.Id)
				CleanupTrans(ctx.pBackConn.Conn)
			}
			if ctx.isGetBackend {
				zap.S().Debugf("Client(%d): release backend(%d)", ctx.Pid, ctx.pBackConn.Id)
				atomic.StoreInt32(&ctx.pBackConn.State, 0)
				ctx.isGetBackend = false
			}
			return err
		}
		//TCHDEBUG zap.S().Infof("Client(%d, B): RC(%c): len=%d, data=%v", ctx.Pid, ctx.recvBuf[0], n, ctx.recvBuf[:n])
		if ctx.recvBuf[0] == 'S' {
			break
		}
	}
	var errFields = [...]string{
		"SFATAL",
		"VFATAL",
		"C26000",
		"Mprepare not exists",
		"Fcstech.go",
		"L781",
		"RBind_error",
	}
	zap.S().Infof("Client(%d, B): SC: Bind error", ctx.Pid)
	err = sendPqErrorResponse(ctx.cliConn, errFields[:])
	if err != nil {
		zap.S().Infof("Client(%d, B): SC: ERROR: %s", ctx.Pid, err.Error())
		ctx.cliConn.Close()
		if ctx.transState != 'I' {
			zap.S().Infof("Client(%d, B): Cleanup backend(%d) transaction", ctx.Pid, ctx.pBackConn.Id)
			CleanupTrans(ctx.pBackConn.Conn)
		}
		if ctx.isGetBackend {
			zap.S().Debugf("Client(%d): release backend(%d)", ctx.Pid, ctx.pBackConn.Id)
			atomic.StoreInt32(&ctx.pBackConn.State, 0)
			ctx.isGetBackend = false
		}
		return errors.New("bind error")
	}
	zap.S().Infof("Client(%d, B): SC: ready for query", ctx.Pid)
	sendPqReadyForQuery(ctx.cliConn, ctx.transState)
	ctx.cliConn.Close()
	if ctx.transState != 'I' {
		zap.S().Infof("Client(%d, B): Cleanup backend(%d) transaction", ctx.Pid, ctx.pBackConn.Id)
		CleanupTrans(ctx.pBackConn.Conn)
	}
	if ctx.isGetBackend {
		zap.S().Debugf("Client(%d): release backend(%d)", ctx.Pid, ctx.pBackConn.Id)
		atomic.StoreInt32(&ctx.pBackConn.State, 0)
		ctx.isGetBackend = false
	}
	return errors.New("unknown prepare name")

}
